#include <SDL2/SDL.h>
#include <SDL2/SDL_image.h>
#include <SDL2/SDL_ttf.h>
#include <iostream>

// 封装SDL_Texture
//Texture wrapper class
class LTexture
{
    public:
        //Initializes variables
        LTexture();

        //Deallocates memory
        ~LTexture();

        //Loads image at specified path
        bool loadFromFile( std::string path );

        //Creates image from font string
        // SDL_ttf 的工作方式是根据字体和颜色创建新图像。对于纹理类来说，这意味着将从 SDL_ttf 渲染的文本而不是文件中加载图像
        bool loadFromRenderedText( std::string textureText, SDL_Color textColor );

        //Deallocates texture
        void free();

        //设置调制颜色，接收红、绿、蓝三色分量
        void setColor( Uint8 red, Uint8 green, Uint8 blue );

        //设置纹理混合  它将控制纹理的混合方式。为了使混合正常工作，必须设置纹理的混合模式
        void setBlendMode( SDL_BlendMode blending );

        //设置纹理的 alpha 透明度
        void setAlpha( Uint8 alpha );

        //Renders texture at given point
        /*接受一个矩形参数，用来定义我们要渲染的纹理部分。我们给它一个默认参数 nullptr，以防我们想要渲染整个纹理
          接受旋转角度、纹理旋转点和 SDL 翻转枚举值，同样也给出了参数的默认值，以备在不旋转或翻转的情况下渲染纹理。
        */
        void render( int x, int y, SDL_Rect* clip = nullptr, double angle = 0.0, SDL_Point* center = nullptr, SDL_RendererFlip flip = SDL_FLIP_NONE );

        //Gets image dimensions
        int getWidth();
        int getHeight();

    private:
        //实际渲染的纹理
        SDL_Texture* mTexture;

        //图像的宽和高
        int mWidth;
        int mHeight;
};

// 渲染的窗口
SDL_Window * gWindow = nullptr;

// 当我们处理 SDL 纹理时，需要一个 SDL_Renderer 将其渲染到屏幕上
//The window renderer
SDL_Renderer* gRenderer = nullptr;

int SCREEN_WIDTH = 640;
int SCREEN_HEIGHT = 480;

//使用的字体
TTF_Font* gFont = nullptr;

LTexture gTextTexture;

LTexture::LTexture()
{
    //Initialize
    mTexture = nullptr;
    mWidth = 0;
    mHeight = 0;
}

LTexture::~LTexture()
{
    //Deallocate
    free();
}

void LTexture::setColor( Uint8 red, Uint8 green, Uint8 blue )
{
    //纹理颜色调制
    /*
    DL_SetTextureColorMod 接受 Uint8 作为颜色组件的参数。Uint8 只是一个无符号的 8 位整数。这意味着它的范围从 0 到 255。
    128 大约介于 0 和 255 之间，因此当您将绿色调制为 128 时，纹理上任何像素的绿色分量都会减半。
    红色和蓝色方格不会受到影响，因为它们没有绿色，但绿色的亮度会减半，白色则会变成浅洋红色（洋红色为红色 255、绿色 0、蓝色 255）。
    颜色调制只是将一种颜色乘以整个纹理的一种方法
    */
    SDL_SetTextureColorMod( mTexture, red, green, blue );
}

bool LTexture::loadFromRenderedText( std::string textureText, SDL_Color textColor )
{
    ////先取消原有纹理的渲染
    free();

    //Render text surface
    /*
    根据字体创建要渲染的纹理。该函数接收要渲染的文本字符串和要用来渲染它的颜色。在此之后，该函数的工作原理与从文件加载相似，
    只不过这次使用的是由 SDL_ttf 创建的 SDL_Surface，而不是文件
    使用 TTF_RenderText_Solid 加载一个曲面。函数会根据给出的字体、文本和颜色创建一个纯色曲面。如果曲面创建成功，
    就会像之前从文件加载曲面一样创建纹理。创建纹理后，我们就可以像渲染其他纹理一样对其进行渲染。
    */
    SDL_Surface* textSurface = TTF_RenderText_Solid( gFont, textureText.c_str(), textColor );
    if( textSurface == nullptr )
    {
        std::cout << "Unable to render text surface! SDL_ttf Error:" << TTF_GetError() << std::endl;
    }
    else
    {
        //Create texture from surface pixels
        mTexture = SDL_CreateTextureFromSurface( gRenderer, textSurface );
        if( mTexture == nullptr )
        {
            std::cout << "Unable to create texture from rendered text! SDL Error:" << SDL_GetError() << std::endl;
        }
        else
        {
            //Get image dimensions
            mWidth = textSurface->w;
            mHeight = textSurface->h;
        }

        //Get rid of old surface
        SDL_FreeSurface( textSurface );
    }
    
    //Return success
    return mTexture != NULL;
}

bool LTexture::loadFromFile( std::string path )
{
    //先取消原有纹理的渲染
    free();

    //The final texture
    SDL_Texture* newTexture = nullptr;

    //Load image at specified path
    SDL_Surface* loadedSurface = IMG_Load( path.c_str() );
    if( loadedSurface == nullptr )
    {
        std::cout<< "Unable to load image "<< path.c_str() << "!, SDL_image Error: " << IMG_GetError() << std::endl;
    }
    else
    {
        /*  使用 SDL_SetColorKey 对图像进行去除背景。第一个参数是我们要进行去除背景处理的表面，第二个参数是我们是否要启用去除背景处理，
        最后一个参数是我们要进行去除背景处理的像素(图像中哪种颜色要去除)
            从 RGB 颜色创建像素的方法是使用 SDL_MapRGB。第一个参数是我们想要的像素格式(SDL_PixelFormat 结构)。幸运的是，载入的曲面中有一个成员变量可以获取该格式。
         最后三个变量是要映射的颜色的红、绿、蓝分量。
         青色 0，255，255
         黑色 0，0，0
         白色 255，255，255
        */
        SDL_SetColorKey( loadedSurface, SDL_TRUE, SDL_MapRGB( loadedSurface->format, 0, 0, 0 ) );

        //Create texture from surface pixels
        newTexture = SDL_CreateTextureFromSurface( gRenderer, loadedSurface );
        if( newTexture == nullptr )
        {
            std::cout << "Unable to create texture from " << path.c_str() << "! SDL Error: " << SDL_GetError() << std::endl;
        }
        else
        {
            //Get image dimensions
            mWidth = loadedSurface->w;
            mHeight = loadedSurface->h;
        }

        //清楚旧纹理加载
        SDL_FreeSurface( loadedSurface );
    }

    //Return success
    mTexture = newTexture;
    return mTexture != nullptr;
}

void LTexture::free()
{
    //Free texture if it exists
    if( mTexture != nullptr )
    {
        SDL_DestroyTexture( mTexture );
        mTexture = nullptr;
        mWidth = 0;
        mHeight = 0;
    }
}

void LTexture::render( int x, int y, SDL_Rect* clip, double angle, SDL_Point* center, SDL_RendererFlip flip )
{
    //设置渲染区域并渲染到屏幕
    SDL_Rect renderQuad = { x, y, mWidth, mHeight };
    /* 在特定位置渲染纹理时，需要指定一个目标矩形，该矩形可设置 x/y 位置和宽度/高度。在不知道原始图像尺寸的情况下，我们无法指定宽度/高度。
    因此，当我们渲染纹理时，我们会创建一个包含位置参数和成员宽度/高度的矩形，并将此矩形传递给 SDL_RenderCopy
    */

    //Set clip rendering dimensions
    if( clip != nullptr )
    {
        renderQuad.w = clip->w;
        renderQuad.h = clip->h;
    }
    /*
    在剪辑时，如果使用的是剪辑矩形的尺寸而不是纹理的尺寸，我们将把目标矩形（此处称为 renderQuad）的宽度/高度设置为剪辑矩形的尺寸。
    我们要将剪辑矩形作为源矩形传递给 SDL_RenderCopy。源矩形定义了要渲染纹理的哪一部分。当源矩形为空时，将渲染整个纹理。
    */

    //Render to screen
    // SDL_RenderCopy( gRenderer, mTexture, clip, &renderQuad );
    //Render to screen 该函数的工作原理与原始的 SDL_RenderCopy 相同，但增加了用于旋转和翻转的参数
    SDL_RenderCopyEx( gRenderer, mTexture, clip, &renderQuad, angle, center, flip );
}

int LTexture::getWidth()
{
    return mWidth;
}

int LTexture::getHeight()
{
    return mHeight;
}

bool init()
{
     //Initialization flag
    bool success = true;

    //Initialize SDL
    if( SDL_Init( SDL_INIT_VIDEO ) < 0 )
    {
        char str[1024];
        sprintf(str, "SDL could not initialize! SDL_Error: %s\n", SDL_GetError());
        std::cout << str << std::endl;
        success = false;
    }
    else
    {
        //Create window
        gWindow = SDL_CreateWindow( "SDL Tutorial", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, SCREEN_WIDTH, SCREEN_HEIGHT, SDL_WINDOW_SHOWN );
        if( gWindow == nullptr )
        {
            char str[1024];
            sprintf(str, "Window could not be created! SDL_Error: %s\n", SDL_GetError());
            std::cout << str << std::endl;
            success = false;
        }
        else
        {
            //Create renderer for window
            // gRenderer = SDL_CreateRenderer( gWindow, -1, SDL_RENDERER_ACCELERATED );
            //为窗口创建垂直同步渲染器
            /*
            垂直同步,VSync 可以让渲染与显示器在垂直刷新时的更新同步进行。在本教程中，它将确保动画的运行速度不会太快。
            大多数显示器的运行速度约为每秒 60 帧，这也是我们的假设。如果你的显示器刷新率不同，这就能解释为什么动画运行得过快或过慢
            */
            gRenderer = SDL_CreateRenderer( gWindow, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC );
            if( gRenderer == nullptr )
            {
                std::cout << "Renderer could not be created! SDL Error:" << SDL_GetError() << std::endl;
                success = false;
            }
            else
            {
                //Initialize renderer color
                SDL_SetRenderDrawColor( gRenderer, 0xFF, 0xFF, 0xFF, 0xFF );

                //Initialize PNG loading
                int imgFlags = IMG_INIT_PNG;
                if( !( IMG_Init( imgFlags ) & imgFlags ) )
                {
                    std::cout << "SDL_image could not initialize! SDL_image Error:" << IMG_GetError() << std::endl;
                    success = false;
                }

                //Initialize SDL_ttf
                if( TTF_Init() == -1 )
                {
                    std::cout << "SDL_ttf could not initialize! SDL_ttf Error:" << TTF_GetError() << std::endl;
                    success = false;
                }
            }
        }
    }

    return success;
}

bool loadMedia()
{
    //Loading success flag
    bool success = true;

    //Open the font
    //使用 TTF_OpenFont 加载字体。这需要输入字体文件的路径和我们要渲染的点尺寸
    gFont = TTF_OpenFont( "lazy.ttf", 28 );
    if( gFont == nullptr )
    {
        std::cout << "Failed to load lazy font! SDL_ttf Error:" << TTF_GetError() << std::endl;
        success = false;
    }
    else
    {
        //Render text r,g,b,a
        SDL_Color textColor = { 0, 0, 0, 0 };
        /*
        如果字体加载成功，我们将使用加载方法加载纹理。一般来说，我们希望尽量减少渲染文本的次数。因为我们在整个程序中使用的是同一个文本表面，所以只需渲染一次
        */
        if( !gTextTexture.loadFromRenderedText( "The quick brown fox jumps over the lazy dog", textColor ) )
        {
            std::cout << "Failed to render text texture!" << std::endl;
            success = false;
        }
    }


    return success;
}

void LTexture::setBlendMode( SDL_BlendMode blending )
{
    //Set blending function
    // SDL_SetTextureBlendMode 允许启用混合模式
    SDL_SetTextureBlendMode( mTexture, blending );
}
        
void LTexture::setAlpha( Uint8 alpha )
{
    //Modulate texture alpha
    // SDL_SetTextureAlphaMod 则允许设置整个纹理的 Alpha 值
    SDL_SetTextureAlphaMod( mTexture, alpha );
}

void close()
{
    // 释放资源  
    gTextTexture.free();

    //Free global font
    TTF_CloseFont( gFont );
    gFont = nullptr;

    //Destroy window
    SDL_DestroyRenderer( gRenderer );
    SDL_DestroyWindow( gWindow );
    gRenderer = nullptr;
    gWindow = nullptr;

    //Quit SDL subsystems
    TTF_Quit();
    IMG_Quit();
    SDL_Quit();
}


int main( int argc, char* args[] )
{
    //Start up SDL and create window
    if( !init() )
    {
        std::cout << "Failed to initialize!" << std::endl;
    }
    else
    {
        //Load media
        if( !loadMedia() )
        {
            std::cout << "Failed to load media!" << std::endl;
        }
        else
        {
            //Hack to get window to stay up
            SDL_Event e; 
            bool quit = false; 

            //Angle of rotation
            double degrees = 0;

            //Flip type
            SDL_RendererFlip flipType = SDL_FLIP_NONE;

            //While application is running
            while( !quit )
            {
                //Handle events on queue
                while( SDL_PollEvent( &e ) != 0 )
                {
                    //User requests quit
                    if( e.type == SDL_QUIT )
                    {
                        quit = true;
                    }
                }
                //Clear screen
                SDL_SetRenderDrawColor( gRenderer, 0xFF, 0xFF, 0xFF, 0xFF );
                SDL_RenderClear( gRenderer );

                //Render current frame
                gTextTexture.render( ( SCREEN_WIDTH - gTextTexture.getWidth() ) / 2, ( SCREEN_HEIGHT - gTextTexture.getHeight() ) / 2 );

                //Update screen
                SDL_RenderPresent( gRenderer );
            }
        }
    }

    //Free resources and close SDL
    close();

    return 0;
}



